/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace DaveSexton.DocProject
{
	[Serializable]
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
	public struct SystemPath : IEquatable<SystemPath>, IEquatable<string>, IComparable<SystemPath>, IComparable<string>,
		IComparable, ICloneable, IEnumerable<char>
	{
		#region Public Properties
		public static readonly SystemPath Empty = new SystemPath();

		/// <summary>
		/// Gets the full path and file name of the <see cref="SystemPath"/>.
		/// </summary>
		/// <remarks>
		/// If the original path contains a recursive directory wildcard, it is not included in the value of the <see cref="Path"/> property.
		/// For example, the value of <see cref="Path"/> for a reference to <em>C:\Projects\**\MyAssembly.exe</em> is <em>C:\Projects\MyAssembly.exe</em>.
		/// Callers can check the <see cref="HasRecursiveDirectory"/> property to determine whether a wildcard was specified in the origional path; 
		/// when <see langword="true"/>, and if the <see cref="SystemPath"/> represents a file, then the <see cref="Directory"/> property will get the 
		/// directory at which recursion should begin.
		/// </remarks>
		/// <value>The full path and file name, without any recursive directory wildcard.</value>
		public string Path { get { return path ?? ""; } }

		/// <summary>
		/// Gets the full path and file name of the <see cref="SystemPath"/>, including a recursive directory wildcard if 
		/// <see cref="HasRecursiveDirectory"/> is <see langword="true"/>.
		/// </summary>
		public string CompletePath
		{
			get
			{
				if (path == null)
					return "";
				else if (hasRecursiveDirectory)
					return (isDirectory)
						? System.IO.Path.Combine(path, PathFormat.WildcardRecursiveDirectorySection)
						: System.IO.Path.Combine(System.IO.Path.Combine(
								PathFormat.SafeGetDirectoryName(path), PathFormat.WildcardRecursiveDirectorySection), System.IO.Path.GetFileName(path));
				else
					return path;
			}
		}

		public Uri Uri
		{
			get
			{
				if (path == null)
					return null;
				else
					return new Uri(path);
			}
		}

		/// <summary>
		/// Gets whether the <see cref="SystemPath"/> is a managed assembly.
		/// </summary>
		public bool IsAssembly { get { return (bool) (isAssembly ?? (isAssembly = IsAssemblyPath(path))); } }

		/// <summary>
		/// Gets whether the <see cref="SystemPath"/> is a directory or a file.
		/// </summary>
		public bool IsDirectory { get { return isDirectory; } }

		/// <summary>
		/// Gets whether the <see cref="SystemPath"/> has one or more file name placeholder characters (e.g., * or ?) or a directory recursion wildcard (e.g., \**\file.txt).
		/// </summary>
		public bool HasWildcard { get { return hasWildcard; } }

		/// <summary>
		/// Gets whether the <see cref="SystemPath"/> represents a file, without any wildcards.
		/// </summary>
		/// <value>Returns <see langword="true"> if <see cref="Path"/> is not <see langword="null"/> and also 
		/// <see cref="IsDirectory"/> and <see cref="HasWildcard"/> are both <see langword="false"/>; otherwise, returns <see langword="false"/>.</value>
		public bool IsAbsoluteFile { get { return path != null && !isDirectory && !hasWildcard; } }

		/// <summary>
		/// Gets whether the <see cref="SystemPath"/> has a directory recursion wildcard; e.g., <em>\**\file.txt</em>, <em>\**\*.exe</em> or <em>\**\</em>)
		/// </summary>
		/// <remarks>
		/// <para>
		/// Only a single directory recursion wildcard may be specified and it must be the most nested directory in the path.  For example, <em>C:\**\Dir\file.txt</em>
		/// will <em>not</em> be interpreted as a directory recursion wildcard.  On the contrary, <em>C:\Dir\**\file.txt</em> uses a valid directory recursion wildcard.
		/// </para>
		/// <para>
		/// A trailing slash is always required as part of the directory recursion wildcard.  If the original path ends with a single asterisk and without a trailing slash, 
		/// .e.g, <em>C:\Dir\*</em>, it is interpreted as a file name wildcard (<em>\*.*</em>).
		/// </para>
		/// </remarks>
		public bool HasRecursiveDirectory { get { return hasRecursiveDirectory; } }

		/// <summary>
		/// Gets the full path to the file, or if the <see cref="SystemPath"/> is a directory then the full path to the parent directory.
		/// </summary>
		public SystemPath Directory
		{
			get
			{
				if (path == null)
					return SystemPath.Empty;
				else
				{
					string directory = PathFormat.SafeGetDirectoryName(path);

					if (directory == null)
						return SystemPath.Empty;
					else
						return new SystemPath(true, directory);
				}
			}
		}

		public System.IO.FileSystemInfo Info
		{
			get
			{
				if (path == null)
					return null;
				else if (isDirectory)
					return new System.IO.DirectoryInfo(path);
				else
					return new System.IO.FileInfo(path);
			}
		}

		public bool Exists
		{
			get
			{
				try
				{
					return (path == null) ? false :
						(hasWildcard)
						? (isDirectory)
							? System.IO.Directory.Exists(path)
							: System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(path))
							&& (hasRecursiveDirectory)
								? ((Predicate<string>) ((p) => System.IO.Directory.GetFiles(System.IO.Path.GetDirectoryName(p), System.IO.Path.GetFileName(p),
										System.IO.SearchOption.AllDirectories).Length > 0))(path)
								: ((Predicate<string>) ((p) => System.IO.Directory.GetFiles(System.IO.Path.GetDirectoryName(p), System.IO.Path.GetFileName(p),
										System.IO.SearchOption.TopDirectoryOnly).Length > 0))(path)
						: (isDirectory)
							? System.IO.Directory.Exists(path)
							: System.IO.File.Exists(path);
				}
				catch (System.IO.DirectoryNotFoundException)
				{
					return false;
				}
			}
		}

		public bool HasFileWildcard
		{
			get
			{
				// NOTE: hasWildcard is also true when a recursive directory wildcard is specified, although it has already been 
				// stripped from the path field.
				return (isDirectory || path == null) ? false : hasWildcard && PathFormat.HasWildcard(path);
			}
		}

		public bool HasFileExtensionWildcard
		{
			get
			{
				return (isDirectory || path == null) ? false : PathFormat.HasWildcard(System.IO.Path.GetExtension(path));
			}
		}

		/// <summary>
		/// Gets the file extension, including the leading period, or <see langword="null"/> if the <see cref="SystemPath"/> 
		/// is a directory or <see cref="Empty"/>.
		/// </summary>
		public string FileExtension
		{
			get
			{
				return (isDirectory || path == null) ? null : System.IO.Path.GetExtension(path);
			}
		}

		/// <summary>
		/// Gets the file extension, including the leading period and without wildcard characters, or <see langword="null"/> 
		/// if the <see cref="SystemPath"/> is a directory or <see cref="Empty"/>.
		/// </summary>
		public string FileExtensionWithoutWildcards
		{
			get
			{
				return (isDirectory || path == null) ? null : PathFormat.RemoveWildcards(System.IO.Path.GetExtension(path));
			}
		}

		/// <summary>
		/// Gets the file name without an extension, or <see langword="null"/> if the <see cref="SystemPath"/> 
		/// is a directory or <see cref="Empty"/>.
		/// </summary>
		public string FileNameWithoutExtension
		{
			get
			{
				return (isDirectory || path == null) ? null : System.IO.Path.GetFileNameWithoutExtension(path);
			}
		}

		/// <summary>
		/// Gets the file name without an extension and wildcard characters, or <see langword="null"/> if the <see cref="SystemPath"/> 
		/// is a directory or <see cref="Empty"/>.
		/// </summary>
		public string FileNameWithoutWildcardsAndExtension
		{
			get
			{
				return (isDirectory || path == null) ? null : PathFormat.RemoveWildcards(System.IO.Path.GetFileNameWithoutExtension(path));
			}
		}

		/// <summary>
		/// Gets the file name and extension, or <see langword="null"/> if the <see cref="SystemPath"/> is a directory 
		/// or <see cref="Empty"/>.
		/// </summary>
		public string FileName
		{
			get
			{
				return (isDirectory || path == null) ? null : System.IO.Path.GetFileName(path);
			}
		}

		/// <summary>
		/// Gets the file name and extension without wildcard characters, or <see langword="null"/> if the <see cref="SystemPath"/> 
		/// is a directory or <see cref="Empty"/>.
		/// </summary>
		public string FileNameWithoutWildcards
		{
			get
			{
				return (isDirectory || path == null) ? null : PathFormat.RemoveWildcards(System.IO.Path.GetFileName(path));
			}
		}

		[System.Diagnostics.DebuggerHidden]
		public AssemblyName AssemblyName
		{
			get
			{
				if (assemblyName == null && IsAssembly && !hasWildcard)
					assemblyName = System.Reflection.AssemblyName.GetAssemblyName(path);

				return assemblyName;
			}
		}

		public string AssemblyOrFileName
		{
			get
			{
				AssemblyName name;
				if (TryGetAssemblyName(out name))
					return name.ToString();
				else
					return FileName;
			}
		}

		public string AssemblyNameOrPath
		{
			get
			{
				AssemblyName name;
				if (TryGetAssemblyName(out name))
					return name.ToString();
				else
					return path ?? "";
			}
		}

		public string AssemblyNameOrCompletePath
		{
			get
			{
				AssemblyName name;
				if (TryGetAssemblyName(out name))
					return name.ToString();
				else
					return CompletePath;
			}
		}
		#endregion

		#region Private / Protected
		private readonly string path;
		private readonly bool isDirectory, hasWildcard, hasRecursiveDirectory;
		private bool? isAssembly;
		private AssemblyName assemblyName;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="SystemPath" /> structure.
		/// </summary>
		public SystemPath(string basePath, string relativePath, bool allowWildcards, bool isDirectory)
			: this((bool?) null, basePath, relativePath, allowWildcards, isDirectory)
		{
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SystemPath" /> structure.
		/// </summary>
		public SystemPath(string basePath, string relativePath, bool allowWildcards)
			: this((bool?) null, basePath, relativePath, allowWildcards)
		{
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SystemPath" /> structure.
		/// </summary>
		public SystemPath(string path, bool allowWildcards, bool isDirectory)
			: this((bool?) null, path, allowWildcards, isDirectory)
		{
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SystemPath" /> structure.
		/// </summary>
		public SystemPath(string path, bool allowWildcards)
			: this((bool?) null, path, allowWildcards)
		{
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SystemPath" /> structure.
		/// </summary>
		public SystemPath(string file, AssemblyName name)
		{
			if (string.IsNullOrEmpty(file))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "file");

			if (name == null)
				throw new ArgumentNullException("name");

			if (!System.IO.Path.IsPathRooted(file))
				throw new ArgumentException(Resources.Errors.AbsolutePathExpected, "file");

			if (!IsAssemblyPath(file))
				throw new ArgumentException(Resources.Errors.AssemblyPathExpected, "file");

			path = PathFormat.MakeLocal(file, out hasWildcard, out hasRecursiveDirectory);

			if (hasWildcard)
				throw new ArgumentException(Resources.Errors.WildcardNotAllowed, "file");

			assemblyName = name;
			isAssembly = true;
			isDirectory = false;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SystemPath" /> structure.
		/// </summary>
		public SystemPath(SystemPath clone)
		{
			path = clone.path;
			isAssembly = clone.isAssembly;
			isDirectory = clone.isDirectory;
			hasWildcard = clone.hasWildcard;
			hasRecursiveDirectory = clone.hasRecursiveDirectory;
			assemblyName = clone.assemblyName;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SystemPath" /> structure.
		/// </summary>
		private SystemPath(bool isDirectory, string path)
		{
			this.path = (isDirectory) ? PathFormat.EnsureDirectory(path) : path;
			this.isDirectory = isDirectory;

			if (isDirectory)
				isAssembly = false;
			else
				isAssembly = null;

			hasWildcard = false;
			hasRecursiveDirectory = false;
			assemblyName = null;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SystemPath" /> structure.
		/// </summary>
		private SystemPath(bool? hasRecursiveDirectory, string basePath, string relativePath, bool allowWildcards, bool isDirectory)
		{
			if (basePath == null || (basePath = basePath.Trim()).Length == 0)
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "basePath");

			if (relativePath == null)
				throw new ArgumentNullException("relativePath");

			if (!System.IO.Path.IsPathRooted(basePath))
				throw new ArgumentException(Resources.Errors.AbsolutePathExpected, "basePath");

			// NOTE: allow relative path to be rooted since Path.Combine (used below) will just ignore the base path so that it can be specified 
			// by the caller without having to first check if relativePath is actually relative.

			relativePath = PathFormat.RemoveLeadingPathSeparator(relativePath).TrimEnd();

			if (isDirectory)
			{
				if (relativePath.Length > 0 && !PathFormat.IsDirectory(relativePath))
				{
					if (!PathFormat.IsValidDirectoryName(System.IO.Path.GetFileName(relativePath)))
						throw new ArgumentException(Resources.Errors.DirectoryExpected, "relativePath");

					relativePath = PathFormat.EnsureDirectory(relativePath);
				}

				this.isDirectory = true;
			}
			else if (relativePath.Length == 0 || PathFormat.IsDirectory(relativePath))
				throw new ArgumentException(Resources.Errors.FileExpected, "relativePath");
			else
				this.isDirectory = false;

			bool originalHasWildcard;

			path = PathFormat.MakeLocal(System.IO.Path.Combine(basePath, relativePath), out originalHasWildcard, out this.hasRecursiveDirectory);

			if (hasRecursiveDirectory.HasValue)
			{
				this.hasRecursiveDirectory = hasRecursiveDirectory.Value;

				if (this.hasRecursiveDirectory)
				{
					hasWildcard = true;
					// NOTE: When hasRecursiveDirectory is true, allowWildcards only applies to the formatted path.
					originalHasWildcard = PathFormat.HasWildcard(path);		// if a recursive directory wildcard was specified, it has already been removed in path by PathFormat.MakeLocal
				}
				else
					hasWildcard = originalHasWildcard;
			}
			else
				hasWildcard = originalHasWildcard;

			if (originalHasWildcard)
			{
				if (!allowWildcards)
					throw new ArgumentException(Resources.Errors.WildcardNotAllowed);
				else if (path.Contains(@"\" + PathFormat.WildcardRecursiveDirectorySection))
					// A recursive directory wildcard for the most nested directory is stripped out by PathFormat.MakeLocal
					// but parent directory recursion is not supported.
					throw new ArgumentException(Resources.Errors.RecursiveDirectoryWildcardInPath);
			}

			if (this.isDirectory)
				isAssembly = false;
			else
				isAssembly = null;

			assemblyName = null;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SystemPath" /> structure.
		/// </summary>
		private SystemPath(bool? hasRecursiveDirectory, string basePath, string relativePath, bool allowWildcards)
		{
			if (basePath == null || (basePath = basePath.Trim()).Length == 0)
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "basePath");

			if (relativePath == null)
				throw new ArgumentNullException("relativePath");

			if (!System.IO.Path.IsPathRooted(basePath))
				throw new ArgumentException(Resources.Errors.AbsolutePathExpected, "basePath");

			// NOTE: allow relative path to be rooted since Path.Combine (used below) will just ignore the base path so that it can be specified 
			// by the caller without having to first check if relativePath is actually relative.

			relativePath = PathFormat.RemoveLeadingPathSeparator(relativePath).TrimEnd();

			if (relativePath.Length == 0 || PathFormat.IsDirectory(relativePath))
				isDirectory = true;
			else if (System.IO.Path.GetExtension(relativePath).Length == 0)
			{
				relativePath = PathFormat.EnsureDirectory(relativePath);
				isDirectory = true;
			}
			else
				isDirectory = false;

			bool originalHasWildcard;

			path = PathFormat.MakeLocal(System.IO.Path.Combine(basePath, relativePath), out originalHasWildcard, out this.hasRecursiveDirectory);

			if (hasRecursiveDirectory.HasValue)
			{
				this.hasRecursiveDirectory = hasRecursiveDirectory.Value;

				if (this.hasRecursiveDirectory)
				{
					hasWildcard = true;
					// NOTE: When hasRecursiveDirectory is true, allowWildcards only applies to the formatted path.
					originalHasWildcard = PathFormat.HasWildcard(path);		// if a recursive directory wildcard was specified, it has already been removed in path by PathFormat.MakeLocal
				}
				else
					hasWildcard = originalHasWildcard;
			}
			else
				hasWildcard = originalHasWildcard;

			if (originalHasWildcard)
			{
				if (!allowWildcards)
					throw new ArgumentException(Resources.Errors.WildcardNotAllowed);
				else if (path.Contains(@"\" + PathFormat.WildcardRecursiveDirectorySection))
					// A recursive directory wildcard for the most nested directory is stripped out by PathFormat.MakeLocal
					// but parent directory recursion is not supported.
					throw new ArgumentException(Resources.Errors.RecursiveDirectoryWildcardInPath);
			}

			if (isDirectory)
				isAssembly = false;
			else
				isAssembly = null;

			assemblyName = null;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SystemPath" /> structure.
		/// </summary>
		private SystemPath(bool? hasRecursiveDirectory, string path, bool allowWildcards, bool isDirectory)
		{
			if (path == null || (path = path.Trim()).Length == 0)
				throw new ArgumentException(Resources.Errors.NullOrEmpty, path);

			if (!System.IO.Path.IsPathRooted(path))
				throw new ArgumentException(Resources.Errors.AbsolutePathExpected, "path");

			if (isDirectory)
			{
				if (!PathFormat.IsDirectory(path))
				{
					if (!PathFormat.IsValidDirectoryName(System.IO.Path.GetFileName(path)))
						throw new ArgumentException(Resources.Errors.DirectoryExpected, "path");

					path = PathFormat.EnsureDirectory(path);
				}

				this.isDirectory = true;
			}
			else if (PathFormat.IsDirectory(path))
				throw new ArgumentException(Resources.Errors.FileExpected, "path");
			else
				this.isDirectory = false;

			bool originalHasWildcard;

			this.path = PathFormat.MakeLocal(path, out originalHasWildcard, out this.hasRecursiveDirectory);

			if (hasRecursiveDirectory.HasValue)
			{
				this.hasRecursiveDirectory = hasRecursiveDirectory.Value;

				if (this.hasRecursiveDirectory)
				{
					hasWildcard = true;
					// NOTE: When hasRecursiveDirectory is true, allowWildcards only applies to the formatted path.
					originalHasWildcard = PathFormat.HasWildcard(path);		// if a recursive directory wildcard was specified, it has already been removed in path by PathFormat.MakeLocal
				}
				else
					hasWildcard = originalHasWildcard;
			}
			else
				hasWildcard = originalHasWildcard;

			if (originalHasWildcard)
			{
				if (!allowWildcards)
					throw new ArgumentException(Resources.Errors.WildcardNotAllowed);
				else if (this.path.Contains(@"\" + PathFormat.WildcardRecursiveDirectorySection))
					// A recursive directory wildcard for the most nested directory is stripped out by PathFormat.MakeLocal
					// but parent directory recursion is not supported.
					throw new ArgumentException(Resources.Errors.RecursiveDirectoryWildcardInPath);
			}

			if (isDirectory)
				isAssembly = false;
			else
				isAssembly = null;

			assemblyName = null;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SystemPath" /> structure.
		/// </summary>
		private SystemPath(bool? hasRecursiveDirectory, string path, bool allowWildcards)
		{
			if (path == null || (path = path.Trim()).Length == 0)
				throw new ArgumentException(Resources.Errors.NullOrEmpty, path);

			if (!System.IO.Path.IsPathRooted(path))
				throw new ArgumentException(Resources.Errors.AbsolutePathExpected, "path");

			if (PathFormat.IsDirectory(path))
				isDirectory = true;
			else if (System.IO.Path.GetExtension(path).Length == 0)
			{
				path = PathFormat.EnsureDirectory(path);
				isDirectory = true;
			}
			else
				isDirectory = false;

			bool originalHasWildcard;

			this.path = PathFormat.MakeLocal(path, out originalHasWildcard, out this.hasRecursiveDirectory);

			if (hasRecursiveDirectory.HasValue)
			{
				this.hasRecursiveDirectory = hasRecursiveDirectory.Value;

				if (this.hasRecursiveDirectory)
				{
					hasWildcard = true;
					// NOTE: When hasRecursiveDirectory is true, allowWildcards only applies to the formatted path.
					originalHasWildcard = PathFormat.HasWildcard(path);		// if a recursive directory wildcard was specified, it has already been removed in path by PathFormat.MakeLocal
				}
				else
					hasWildcard = originalHasWildcard;
			}
			else
				hasWildcard = originalHasWildcard;

			if (originalHasWildcard)
			{
				if (!allowWildcards)
					throw new ArgumentException(Resources.Errors.WildcardNotAllowed);
				else if (this.path.Contains(@"\" + PathFormat.WildcardRecursiveDirectorySection))
					// A recursive directory wildcard for the most nested directory is stripped out by PathFormat.MakeLocal
					// but parent directory recursion is not supported.
					throw new ArgumentException(Resources.Errors.RecursiveDirectoryWildcardInPath);
			}

			if (isDirectory)
				isAssembly = false;
			else
				isAssembly = null;

			assemblyName = null;
		}
		#endregion

		#region Methods
		#region Static Construction
		public static SystemPath DirectoryOf(string basePath, string relativePath, bool allowWildcards)
		{
			return new SystemPath(null, basePath, System.IO.Path.GetDirectoryName(relativePath), allowWildcards, true);
		}

		public static SystemPath DirectoryOf(string path, bool allowWildcards)
		{
			return new SystemPath((bool?) null, System.IO.Path.GetDirectoryName(path), allowWildcards, true);
		}

		public static SystemPath Recursive(string basePath, string relativePath, bool allowWildcards, bool isDirectory)
		{
			return new SystemPath(true, basePath, relativePath, allowWildcards, isDirectory);
		}

		public static SystemPath Recursive(string basePath, string relativePath, bool allowWildcards)
		{
			return new SystemPath(true, basePath, relativePath, allowWildcards);
		}

		public static SystemPath Recursive(string path, bool allowWildcards, bool isDirectory)
		{
			return new SystemPath(true, path, allowWildcards, isDirectory);
		}

		public static SystemPath Recursive(string path, bool allowWildcards)
		{
			return new SystemPath(true, path, allowWildcards);
		}

		public static SystemPath NotRecursive(string basePath, string relativePath, bool allowWildcards, bool isDirectory)
		{
			return new SystemPath(false, basePath, relativePath, allowWildcards, isDirectory);
		}

		public static SystemPath NotRecursive(string basePath, string relativePath, bool allowWildcards)
		{
			return new SystemPath(false, basePath, relativePath, allowWildcards);
		}

		public static SystemPath NotRecursive(string path, bool allowWildcards, bool isDirectory)
		{
			return new SystemPath(false, path, allowWildcards, isDirectory);
		}

		public static SystemPath NotRecursive(string path, bool allowWildcards)
		{
			return new SystemPath(false, path, allowWildcards);
		}

		public static SystemPath Combine(bool allowWildcards, bool isDirectory, params string[] parts)
		{
			return new SystemPath(Combine(parts), allowWildcards, isDirectory);
		}

		public static SystemPath Combine(bool allowWildcards, params string[] parts)
		{
			return new SystemPath(Combine(parts), allowWildcards);
		}

		private static string Combine(params string[] parts)
		{
			if (parts == null)
				throw new ArgumentNullException("parts");

			if (parts.Length == 0)
				throw new ArgumentOutOfRangeException("parts", Resources.Errors.SystemPathNoParts);

			string path = null;

			foreach (string part in parts)
			{
				string trimmedPart = PathFormat.RemoveLeadingPathSeparator(part);

				if (trimmedPart == null || (trimmedPart = trimmedPart.TrimEnd()).Length == 0)
					continue;

				if (path == null)
					path = trimmedPart;
				else
					path = System.IO.Path.Combine(path, trimmedPart);
			}

			if (string.IsNullOrEmpty(path))
				throw new ArgumentException(Resources.Errors.SystemPathNoValidParts, "parts");

			return path;
		}
		#endregion

		#region Paths
		/// <summary>
		/// Makes the <see cref="SystemPath"/> relative to the specified <paramref name="basePath"/>.
		/// </summary>
		public string RelativeTo(string basePath)
		{
			return (path == null) ? null : PathFormat.MakeRelativeLocal(basePath, path);
		}

		/// <summary>
		/// Makes the <see cref="SystemPath"/> relative to the specified <paramref name="basePath"/> and includes a 
		/// recursive directory wildcard if <see cref="HasRecursiveDirectory"/> is <see langword="true"/>.
		/// </summary>
		public string CompleteRelativeTo(string basePath)
		{
			if (path == null)
				return null;

			string relativePath = PathFormat.MakeRelativeLocal(basePath, path);

			if (hasRecursiveDirectory)
				return (isDirectory)
					? System.IO.Path.Combine(relativePath, PathFormat.WildcardRecursiveDirectorySection)
					: System.IO.Path.Combine(System.IO.Path.Combine(
							PathFormat.SafeGetDirectoryName(relativePath), PathFormat.WildcardRecursiveDirectorySection), System.IO.Path.GetFileName(relativePath));
			else
				return relativePath;
		}

		/// <summary>
		/// Combines the <see cref="SystemPath"/> with the specified <paramref name="relativePath"/>.
		/// </summary>
		public string CombineWith(string relativePath)
		{
			if (path == null)
				return null;
			else if (relativePath == null || (relativePath = relativePath.Trim()).Length == 0)
				return path;
			else if (isDirectory)
				return System.IO.Path.Combine(path, PathFormat.RemoveLeadingPathSeparator(relativePath));
			else if (PathFormat.IsDirectory(relativePath))
				return System.IO.Path.Combine(System.IO.Path.Combine(
					PathFormat.SafeGetDirectoryName(path), PathFormat.RemoveLeadingPathSeparator(relativePath)), System.IO.Path.GetFileName(path));
			else
				// two file paths cannot be combined without loss of data
				throw new InvalidOperationException(Resources.Errors.SystemPathNotDirectory);
		}

		/// <summary>
		/// Combines the <see cref="SystemPath"/> with the specified <paramref name="relativePath"/>, including a 
		/// recursive directory wildcard if <see cref="HasRecursiveDirectory"/> is <see langword="true"/>.
		/// </summary>
		public string CompleteCombineWith(string relativePath)
		{
			if (!hasRecursiveDirectory)
				return CombineWith(relativePath);
			else if (isDirectory)
			{
				if (PathFormat.IsDirectory(relativePath))
					return System.IO.Path.Combine(System.IO.Path.Combine(path, PathFormat.RemoveLeadingPathSeparator(relativePath)), PathFormat.WildcardRecursiveDirectorySection);
				else
					return System.IO.Path.Combine(System.IO.Path.Combine(System.IO.Path.Combine(
						path, PathFormat.RemoveLeadingPathSeparator(PathFormat.SafeGetDirectoryName(relativePath))), PathFormat.WildcardRecursiveDirectorySection), System.IO.Path.GetFileName(relativePath));
			}
			else if (PathFormat.IsDirectory(relativePath))
				return System.IO.Path.Combine(System.IO.Path.Combine(System.IO.Path.Combine(
					PathFormat.SafeGetDirectoryName(path), PathFormat.RemoveLeadingPathSeparator(relativePath)), PathFormat.WildcardRecursiveDirectorySection), System.IO.Path.GetFileName(path));
			else
				// two file paths cannot be combined without loss of data
				throw new InvalidOperationException(Resources.Errors.SystemPathNotDirectory);
		}

		public static bool IsAssemblyPath(string path)
		{
			string extension = System.IO.Path.GetExtension(path);

			return !string.IsNullOrEmpty(extension) &&
				(string.Equals(extension, ".dll", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".exe", StringComparison.OrdinalIgnoreCase));
		}
		#endregion

		#region Change Tracking
		public bool HasChangedSince(DateTime dateTime)
		{
			return HasChangedSince(dateTime, true, isDirectory);
		}

		public bool HasChangedSince(DateTime dateTime, bool checkFiles, bool checkDirectories)
		{
			if (!checkFiles && !checkDirectories)
				throw new ArgumentException(Resources.Errors.SystemPathNoCriteriaToCheck);

			if (path == null)
				return false;
			else if ((checkDirectories && isDirectory) || (checkFiles && !isDirectory && !hasWildcard))
			{
				// if the file or directory doesn't exist, LastWriteTime will return a very old date (not DateTime.MinValue though, according to MSDN)
				if (Info.LastWriteTime > dateTime)
					return true;
				else if (!isDirectory)
					return false;
			}

			if (checkDirectories)
			{
				if (checkFiles)
				{
					foreach (System.IO.FileSystemInfo info in GetFileSystemInfos())
						if (info.LastWriteTime > dateTime)
							return true;
				}
				else
				{
					foreach (System.IO.DirectoryInfo directory in GetDirectoryInfos())
						if (directory.LastWriteTime > dateTime)
							return true;
				}
			}
			else if (checkFiles)
			{
				foreach (System.IO.FileInfo file in GetFileInfos())
					if (file.LastWriteTime > dateTime)
						return true;
			}

			return false;
		}

		public bool HasChangedSince(DateTime dateTime, string fileSearchPattern)
		{
			return HasChangedSince(dateTime, "*", fileSearchPattern, hasRecursiveDirectory, true);
		}

		public bool HasChangedSince(DateTime dateTime, string fileSearchPattern, bool recursive)
		{
			return HasChangedSince(dateTime, "*", fileSearchPattern, recursive, true);
		}

		public bool HasChangedSince(DateTime dateTime, string fileSearchPattern, bool recursive, bool checkDirectories)
		{
			return HasChangedSince(dateTime, "*", fileSearchPattern, recursive, checkDirectories);
		}

		public bool HasChangedSince(DateTime dateTime, string directorySearchPattern, string fileSearchPattern)
		{
			return HasChangedSince(dateTime, directorySearchPattern, fileSearchPattern, hasRecursiveDirectory, true);
		}

		public bool HasChangedSince(DateTime dateTime, string directorySearchPattern, string fileSearchPattern, bool recursive)
		{
			return HasChangedSince(dateTime, directorySearchPattern, fileSearchPattern, recursive, true);
		}

		public bool HasChangedSince(DateTime dateTime, string directorySearchPattern, string fileSearchPattern, bool recursive, bool checkDirectories)
		{
			if (string.IsNullOrEmpty(directorySearchPattern))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "directorySearchPattern");

			if (fileSearchPattern != null && fileSearchPattern.Length == 0)
				throw new ArgumentException(Resources.Errors.EmptyString, "fileSearchPattern");

			if (fileSearchPattern == null && !checkDirectories)
				throw new ArgumentException(Resources.Errors.SystemPathNoCriteriaToCheck);

			if (path == null)
				return false;

			if (!isDirectory)
				throw new InvalidOperationException(Resources.Errors.SystemPathNotDirectory);

			if (checkDirectories)
			{
				// if the directory doesn't exist, LastWriteTime will return a very old date (not DateTime.MinValue though, according to MSDN)
				if (Info.LastWriteTime > dateTime)
					return true;

				if (fileSearchPattern != null)
				{
					foreach (System.IO.FileSystemInfo info in GetFileSystemInfos(directorySearchPattern, fileSearchPattern, recursive))
						if (info.LastWriteTime > dateTime)
							return true;
				}
				else
				{
					foreach (System.IO.DirectoryInfo directory in GetDirectoryInfos(directorySearchPattern, recursive))
						if (directory.LastWriteTime > dateTime)
							return true;
				}
			}
			else if (fileSearchPattern != null)
			{
				foreach (System.IO.FileInfo file in GetFileInfos(directorySearchPattern, fileSearchPattern, recursive))
					if (file.LastWriteTime > dateTime)
						return true;
			}

			return false;
		}
		#endregion

		#region Searching
		public IEnumerable<SystemPath> GetFiles()
		{
			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (hasWildcard || isDirectory)
			{
				foreach (string file in System.IO.Directory.GetFiles(
					(isDirectory) ? path : System.IO.Path.GetDirectoryName(path),
					(isDirectory) ? "*.*" : System.IO.Path.GetFileName(path),
					(hasRecursiveDirectory) ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly))
				{
					yield return new SystemPath(false, file);
				}
			}
			else
				yield return this;
		}

		public IEnumerable<SystemPath> GetFiles(string fileSearchPattern)
		{
			return GetFiles(fileSearchPattern, hasRecursiveDirectory);
		}

		public IEnumerable<SystemPath> GetFiles(string fileSearchPattern, bool recursive)
		{
			if (string.IsNullOrEmpty(fileSearchPattern))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "searchPattern");

			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (!isDirectory)
				throw new InvalidOperationException(Resources.Errors.SystemPathNotDirectory);

			foreach (string file in System.IO.Directory.GetFiles(path, fileSearchPattern,
				(recursive) ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly))
			{
				yield return new SystemPath(false, file);
			}
		}

		public IEnumerable<string> GetFilePaths()
		{
			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (hasWildcard || isDirectory)
			{
				foreach (string file in System.IO.Directory.GetFiles(
					(isDirectory) ? path : System.IO.Path.GetDirectoryName(path),
					(isDirectory) ? "*.*" : System.IO.Path.GetFileName(path),
					(hasRecursiveDirectory) ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly))
				{
					yield return file;
				}
			}
			else
				yield return this.Path;
		}

		public IEnumerable<string> GetFilePaths(string fileSearchPattern)
		{
			return GetFilePaths(fileSearchPattern, hasRecursiveDirectory);
		}

		public IEnumerable<string> GetFilePaths(string fileSearchPattern, bool recursive)
		{
			if (string.IsNullOrEmpty(fileSearchPattern))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "fileSearchPattern");

			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (!isDirectory)
				throw new InvalidOperationException(Resources.Errors.SystemPathNotDirectory);

			foreach (string file in System.IO.Directory.GetFiles(path, fileSearchPattern,
				(recursive) ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly))
			{
				yield return file;
			}
		}

		public IEnumerable<string> GetFileNames()
		{
			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (hasWildcard || isDirectory)
			{
				foreach (string file in System.IO.Directory.GetFiles(
					(isDirectory) ? path : System.IO.Path.GetDirectoryName(path),
					(isDirectory) ? "*.*" : System.IO.Path.GetFileName(path),
					(hasRecursiveDirectory) ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly))
				{
					yield return System.IO.Path.GetFileName(file);
				}
			}
			else
				yield return this.FileName;
		}

		public IEnumerable<string> GetFileNames(string fileSearchPattern)
		{
			return GetFileNames(fileSearchPattern, hasRecursiveDirectory);
		}

		public IEnumerable<string> GetFileNames(string fileSearchPattern, bool recursive)
		{
			if (string.IsNullOrEmpty(fileSearchPattern))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "fileSearchPattern");

			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (!isDirectory)
				throw new InvalidOperationException(Resources.Errors.SystemPathNotDirectory);

			foreach (string file in System.IO.Directory.GetFiles(path, fileSearchPattern,
				(recursive) ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly))
			{
				yield return System.IO.Path.GetFileName(file);
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.FileInfo> GetFileInfos()
		{
			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (hasWildcard || isDirectory)
			{
				System.IO.DirectoryInfo directory = new System.IO.DirectoryInfo((isDirectory) ? path : System.IO.Path.GetDirectoryName(path));
				string searchPattern = (isDirectory) ? "*.*" : System.IO.Path.GetFileName(path);

				foreach (System.IO.FileInfo file in directory.GetFiles(searchPattern,
					(hasRecursiveDirectory) ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly))
				{
					yield return file;
				}
			}
			else
				yield return new System.IO.FileInfo(this.FileName);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.FileInfo> GetFileInfos(string fileSearchPattern)
		{
			return GetFileInfos(fileSearchPattern, hasRecursiveDirectory);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.FileInfo> GetFileInfos(string fileSearchPattern, bool recursive)
		{
			if (string.IsNullOrEmpty(fileSearchPattern))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "fileSearchPattern");

			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (!isDirectory)
				throw new InvalidOperationException(Resources.Errors.SystemPathNotDirectory);

			System.IO.DirectoryInfo directory = new System.IO.DirectoryInfo(path);

			// NOTE: The documentation for the GetFiles searchPattern parameter is invalid - it's only used to match file names, not directory names
			foreach (System.IO.FileInfo file in directory.GetFiles(fileSearchPattern,
				(recursive) ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly))
			{
				yield return file;
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.FileInfo> GetFileInfos(string directorySearchPattern, string fileSearchPattern)
		{
			return GetFileInfos(directorySearchPattern, fileSearchPattern, hasRecursiveDirectory);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.FileInfo> GetFileInfos(string directorySearchPattern, string fileSearchPattern, bool recursive)
		{
			if (string.IsNullOrEmpty(directorySearchPattern))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "directorySearchPattern");

			if (string.IsNullOrEmpty(fileSearchPattern))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "fileSearchPattern");

			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (!isDirectory)
				throw new InvalidOperationException(Resources.Errors.SystemPathNotDirectory);

			System.IO.DirectoryInfo directory = new System.IO.DirectoryInfo(path);

			foreach (System.IO.FileSystemInfo info in GetFileSystemInfos(directory, directorySearchPattern, fileSearchPattern, recursive))
			{
				System.IO.FileInfo file = info as System.IO.FileInfo;

				if (file != null)
					yield return file;
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.DirectoryInfo> GetDirectoryInfos()
		{
			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (!isDirectory)
				throw new InvalidOperationException(Resources.Errors.SystemPathNotDirectory);

			System.IO.DirectoryInfo directory = new System.IO.DirectoryInfo(path);

			foreach (System.IO.DirectoryInfo subDirectory in directory.GetDirectories("*",
				(hasRecursiveDirectory) ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly))
			{
				yield return subDirectory;
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.DirectoryInfo> GetDirectoryInfos(string directorySearchPattern)
		{
			return GetDirectoryInfos(directorySearchPattern, hasRecursiveDirectory);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.DirectoryInfo> GetDirectoryInfos(string directorySearchPattern, bool recursive)
		{
			if (string.IsNullOrEmpty(directorySearchPattern))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "directorySearchPattern");

			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (!isDirectory)
				throw new InvalidOperationException(Resources.Errors.SystemPathNotDirectory);

			System.IO.DirectoryInfo directory = new System.IO.DirectoryInfo(path);

			foreach (System.IO.DirectoryInfo subDirectory in directory.GetDirectories(directorySearchPattern,
				(recursive) ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly))
			{
				yield return subDirectory;
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.FileSystemInfo> GetFileSystemInfos()
		{
			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (hasWildcard || isDirectory)
			{
				System.IO.DirectoryInfo directory = new System.IO.DirectoryInfo((isDirectory) ? path : System.IO.Path.GetDirectoryName(path));
				string fileSearchPattern = (isDirectory) ? "*.*" : System.IO.Path.GetFileName(path);

				foreach (System.IO.FileSystemInfo info in GetFileSystemInfos(directory, "*", fileSearchPattern, hasRecursiveDirectory))
					yield return info;
			}
			else
				yield return new System.IO.FileInfo(this.FileName);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.FileSystemInfo> GetFileSystemInfos(string fileSearchPattern)
		{
			return GetFileSystemInfos("*", fileSearchPattern, hasRecursiveDirectory);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.FileSystemInfo> GetFileSystemInfos(string fileSearchPattern, bool recursive)
		{
			return GetFileSystemInfos("*", fileSearchPattern, recursive);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.FileSystemInfo> GetFileSystemInfos(string directorySearchPattern, string fileSearchPattern)
		{
			return GetFileSystemInfos(directorySearchPattern, fileSearchPattern, hasRecursiveDirectory);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		public IEnumerable<System.IO.FileSystemInfo> GetFileSystemInfos(string directorySearchPattern, string fileSearchPattern, bool recursive)
		{
			if (string.IsNullOrEmpty(directorySearchPattern))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "directorySearchPattern");

			if (string.IsNullOrEmpty(fileSearchPattern))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "fileSearchPattern");

			if (path == null)
				throw new InvalidOperationException(Resources.Errors.EmptySystemPathIsInvalid);

			if (!isDirectory)
				throw new InvalidOperationException(Resources.Errors.SystemPathNotDirectory);

			System.IO.DirectoryInfo directory = new System.IO.DirectoryInfo(path);

			foreach (System.IO.FileSystemInfo info in GetFileSystemInfos(directory, directorySearchPattern, fileSearchPattern, recursive))
				yield return info;
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Infos")]
		private IEnumerable<System.IO.FileSystemInfo> GetFileSystemInfos(System.IO.DirectoryInfo directory, string directorySearchPattern, string fileSearchPattern, bool recursive)
		{
			foreach (System.IO.DirectoryInfo subDirectory in directory.GetDirectories(directorySearchPattern))
			{
				if (recursive)
					foreach (System.IO.FileSystemInfo info in GetFileSystemInfos(subDirectory, directorySearchPattern, fileSearchPattern, true))
						yield return info;

				yield return subDirectory;
			}

			// NOTE: The documentation for the GetFiles searchPattern parameter is invalid - it's only used to match file names, not directory names
			foreach (System.IO.FileInfo file in directory.GetFiles(fileSearchPattern, System.IO.SearchOption.TopDirectoryOnly))
				yield return file;
		}
		#endregion

		#region File System Management
		public void CopyTo(SystemPath target, bool overwrite)
		{
			if (!target.isDirectory)
			{
				if (isDirectory || hasWildcard)
					throw new ArgumentException(Resources.Errors.SystemPathNotDirectory, "target");

				System.IO.File.Copy(path, target.path, overwrite);
				return;
			}

			if (target.hasWildcard)
				throw new ArgumentException(Resources.Errors.WildcardNotAllowed, "target");

			foreach (string file in GetFilePaths())
				System.IO.File.Copy(file, target.CombineWith(System.IO.Path.GetFileName(file)), overwrite);
		}

		public void CopyTo(SystemPath target, string newFileExtension, bool overwrite)
		{
			if (!target.isDirectory)
				throw new ArgumentException(Resources.Errors.SystemPathNotDirectory, "target");

			if (target.hasWildcard)
				throw new ArgumentException(Resources.Errors.WildcardNotAllowed, "target");

			foreach (string file in GetFilePaths())
				System.IO.File.Copy(file, target.CombineWith(
					System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(file), newFileExtension)), overwrite);
		}

		[System.Diagnostics.DebuggerHidden]
		public void CopyTo(SystemPath target, bool overwrite, bool ensureDirectory)
		{
			bool tryAgain;

			do
			{
				tryAgain = false;

				try
				{
					CopyTo(target, overwrite);
				}
				catch (System.IO.DirectoryNotFoundException)
				{
					if (!ensureDirectory)
						throw;

					System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(target.path));
					tryAgain = true;
				}
			}
			while (tryAgain);
		}

		[System.Diagnostics.DebuggerHidden]
		public void CopyTo(SystemPath target, string newFileExtension, bool overwrite, bool ensureDirectory)
		{
			bool tryAgain;

			do
			{
				tryAgain = false;

				try
				{
					CopyTo(target, newFileExtension, overwrite);
				}
				catch (System.IO.DirectoryNotFoundException)
				{
					if (!ensureDirectory)
						throw;

					System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(target.path));
					tryAgain = true;
				}
			}
			while (tryAgain);
		}
		#endregion

		public bool TryGetAssemblyName(out AssemblyName name)
		{
			try
			{
				name = AssemblyName;
				return name != null;
			}
			catch (ArgumentException ex)
			{
				System.Diagnostics.Debug.WriteLine(ex.ToString(), "Error in SystemPath.TryGetAssemblyName: " + path);
			}
			catch (BadImageFormatException ex)
			{
				System.Diagnostics.Debug.WriteLine(ex.ToString(), "Error in SystemPath.TryGetAssemblyName: " + path);
			}
			catch (System.IO.FileNotFoundException ex)
			{
				System.Diagnostics.Debug.WriteLine(ex.ToString(), "Error in SystemPath.TryGetAssemblyName: " + path);
			}
			catch (System.IO.FileLoadException ex)
			{
				System.Diagnostics.Debug.WriteLine(ex.ToString(), "Error in SystemPath.TryGetAssemblyName: " + path);
			}

			name = null;
			return false;
		}

		public bool HasFileExtension(string extension)
		{
			return (isDirectory) ? false : string.Equals(System.IO.Path.GetExtension(path), extension, StringComparison.OrdinalIgnoreCase);
		}

		public bool HasFileExtension(IEnumerable<string> possibleExtensions)
		{
			if (isDirectory || possibleExtensions == null)
				return false;

			string ext = System.IO.Path.GetExtension(path);

			if (string.IsNullOrEmpty(ext))
				return false;

			foreach (string extension in possibleExtensions)
				if (string.Equals(ext, extension, StringComparison.OrdinalIgnoreCase))
					return true;

			return false;
		}

		public bool HasFileExtensionWithoutWildcards(IEnumerable<string> possibleExtensions)
		{
			if (isDirectory || possibleExtensions == null)
				return false;

			string ext = PathFormat.RemoveWildcards(System.IO.Path.GetExtension(path));

			if (string.IsNullOrEmpty(ext))
				return false;

			foreach (string extension in possibleExtensions)
				if (string.Equals(ext, extension, StringComparison.OrdinalIgnoreCase))
					return true;

			return false;
		}

		public bool HasFileExtensionWithoutWildcards(string extension)
		{
			return (isDirectory) ? false : string.Equals(PathFormat.RemoveWildcards(System.IO.Path.GetExtension(path)), extension, StringComparison.OrdinalIgnoreCase);
		}

		public SystemPath ChangeExtension(string newExtension)
		{
			return new SystemPath(System.IO.Path.ChangeExtension(path, newExtension));
		}

		public override string ToString()
		{
			return CompletePath;
		}
		#endregion

		#region Conversion Methods
		public static implicit operator string(SystemPath path)
		{
			return path.CompletePath;
		}

		public static implicit operator SystemPath(string path)
		{
			return new SystemPath(path, true);
		}
		#endregion

		#region Equals Methods
		public override int GetHashCode()
		{
			return (path ?? "").GetHashCode();
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
		public override bool Equals(object obj)
		{
			return (obj is SystemPath && Equals((SystemPath) obj))
				|| (obj is string && Equals((string) obj));
		}

		public bool Equals(SystemPath other)
		{
			return string.Equals(path, other.path, StringComparison.OrdinalIgnoreCase);
		}

		public bool Equals(string other)
		{
			return (string.IsNullOrEmpty(other)) ? path == null : Equals((SystemPath) other);
		}

		public static bool Equals(string path1, string path2)
		{
			return (string.IsNullOrEmpty(path1))
				? string.IsNullOrEmpty(path2)
				: ((SystemPath) path1).Equals(path2);
		}

		public static bool Equals(SystemPath path1, SystemPath path2)
		{
			return path1.Equals(path2);
		}

		public static bool operator ==(SystemPath info1, SystemPath info2)
		{
			return info1.Equals(info2);
		}

		public static bool operator !=(SystemPath info1, SystemPath info2)
		{
			return !info1.Equals(info2);
		}
		#endregion

		#region Comparison Methods
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
		public int CompareTo(object obj)
		{
			return (obj is SystemPath)
				? CompareTo((SystemPath) obj)
				: (obj is string)
					? CompareTo((string) obj)
					: Comparer<object>.Default.Compare(this, obj);
		}

		public int CompareTo(SystemPath other)
		{
			return string.Compare(path, other.path, StringComparison.OrdinalIgnoreCase);
		}

		public int CompareTo(string other)
		{
			return string.Compare(path, other, StringComparison.OrdinalIgnoreCase);
		}

		public static bool operator <(SystemPath path1, SystemPath path2)
		{
			return path1.CompareTo(path2) < 0;
		}

		public static bool operator >(SystemPath path1, SystemPath path2)
		{
			return path1.CompareTo(path2) > 0;
		}
		#endregion

		#region ICloneable Members
		public object Clone()
		{
			return new SystemPath(this);
		}
		#endregion

		#region IEnumerable<char> Members
		public IEnumerator<char> GetEnumerator()
		{
			return (path ?? "").GetEnumerator();
		}
		#endregion

		#region IEnumerable Members
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return (path ?? "").GetEnumerator();
		}
		#endregion
	}
}
